Transfer any new data

source(file.path(my_working_dir,"run_downloader.R"))
## Added 2 new Ironclad run files
## Added 2 new Silent run files
## Added 1 new Defect run files
## Added 0 new Watcher run files

Load run files into R as JSONs

Filtering out these runs: * Non ascension 20 runs * Runs that ended before floor 6 (considering these not serious runs) * Runs that were victorious but didn’t defeat the heart

storage_dir = file.path(my_working_dir,"all_run_history/")

#make list of all valid ironclad runs
ironclad_list = list()
ironclad_files = list.files(file.path(storage_dir,"IRONCLAD"),full.names=T)
for(fn in ironclad_files){
  result = fromJSON(file = fn)
  if(result$ascension_level == 20 && result$floor_reached >= 6){
    if(!(result$victory == TRUE && result$floor_reached < 52)){
      ironclad_list[result$play_id] = list(result)
    }
  }
}
cat(sprintf("%s total Ironclad runs\n",length(ironclad_list)))
## 47 total Ironclad runs
#make list of all valid silent runs
silent_list = list()
silent_files = list.files(file.path(storage_dir,"THE_SILENT"),full.names=T)
for(fn in silent_files){
  result = fromJSON(file = fn)
  if(result$ascension_level == 20 && result$floor_reached >= 6){
    if(!(result$victory == TRUE && result$floor_reached < 52)){
      silent_list[result$play_id] = list(result)
    }
  }
}
cat(sprintf("%s total Silent runs\n",length(silent_list)))
## 67 total Silent runs
#make list of all valid defect runs
defect_list = list()
defect_files = list.files(file.path(storage_dir,"DEFECT"),full.names=T)
for(fn in defect_files){
  result = fromJSON(file = fn)
  if(result$ascension_level == 20 && result$floor_reached >= 6){
    if(!(result$victory == TRUE && result$floor_reached < 52)){
      defect_list[result$play_id] = list(result)
    }
  }
}
cat(sprintf("%s total Defect runs\n",length(defect_list)))
## 61 total Defect runs
#make list of all valid watcher runs
watcher_list = list()
watcher_files = list.files(file.path(storage_dir,"WATCHER"),full.names=T)
for(fn in watcher_files){
  result = fromJSON(file = fn)
  if(result$ascension_level == 20 && result$floor_reached >= 6){
    if(!(result$victory == TRUE && result$floor_reached < 52)){
      watcher_list[result$play_id] = list(result)
    }
  }
}
cat(sprintf("%s total Watcher runs\n",length(watcher_list)))
## 16 total Watcher runs

Group the data together (preliminary)

#make table for each character with all the 1D attributes (will ignore other stuff for now)
onedim_cols = c("floor_reached","victory","killed_by","local_time","neow_cost","neow_bonus","campfire_rested","campfire_upgraded",
                "purchased_purges","player_experience","gold","playtime","seed_played","score","timestamp")

ironclad_tab = rbindlist(lapply(ironclad_list, function(x){
  x[onedim_cols]
}),fill=T)
## Warning in rbindlist(lapply(ironclad_list, function(x) {: Column 3 ['NA'] of
## item 5 is length 0. This (and 9 others like it) has been filled with NA (NULL
## for list columns) to make each item uniform.
silent_tab = rbindlist(lapply(silent_list, function(x){
  x[onedim_cols]
}),fill=T)
## Warning in rbindlist(lapply(silent_list, function(x) {: Column 3 ['NA'] of item
## 18 is length 0. This (and 11 others like it) has been filled with NA (NULL for
## list columns) to make each item uniform.
defect_tab = rbindlist(lapply(defect_list, function(x){
  x[onedim_cols]
}),fill=T)
## Warning in rbindlist(lapply(defect_list, function(x) {: Column 3 ['NA'] of item
## 1 is length 0. This (and 11 others like it) has been filled with NA (NULL for
## list columns) to make each item uniform.
watcher_tab = rbindlist(lapply(watcher_list, function(x){
  x[onedim_cols]
}),fill=T)
## Warning in rbindlist(lapply(watcher_list, function(x) {: Column 3 ['NA'] of item
## 1 is length 0. This (and 6 others like it) has been filled with NA (NULL for
## list columns) to make each item uniform.
ironclad_tab$character = "Ironclad"
silent_tab$character = "Silent"
defect_tab$character = "Defect"
watcher_tab$character = "Watcher"

#sort with newest runs on top
ironclad_tab = ironclad_tab[order(timestamp,decreasing=T)]
silent_tab = silent_tab[order(timestamp,decreasing=T)]
defect_tab = defect_tab[order(timestamp,decreasing=T)]
watcher_tab = watcher_tab[order(timestamp,decreasing=T)]

#put them into one list
allchar_list = list("Ironclad"=ironclad_tab, "Silent"=silent_tab, "Defect"=defect_tab, "Watcher"=watcher_tab)

For a first pass, I’m just going to output my overall winrate per character/rotating and my winrate per character/rotating over the last 25 and 50 runs

char_vec = c("Ironclad","Silent","Defect","Watcher")
all_timestamps = c()
df = setNames(data.frame(matrix(ncol = 5, nrow = 3)), c(char_vec,"Rotating"))
rownames(df) = c("Overall","Last50","Last25")

for(char in char_vec){
  mychar_tab = allchar_list[[char]]
  all_timestamps = c(all_timestamps,mychar_tab[,timestamp])
  tot = nrow(mychar_tab)
  win_all = sum(mychar_tab[,victory])/tot
  win_50 = sum(mychar_tab[,victory][1:min(50,tot)])/min(50,tot)
  win_25 = sum(mychar_tab[,victory][1:min(25,tot)])/min(25,tot)
  df[char] = c(win_all,win_50,win_25)
}

#get rotating stats
all_timestamps = sort(all_timestamps,decreasing=T)
tot_curr = winsall_curr = wins50_curr = wins25_curr = 0

for(char in char_vec){
  mychar_tab = allchar_list[[char]]
  tot_curr = tot_curr + nrow(mychar_tab)
  winsall_curr = winsall_curr + sum(mychar_tab$victory)
  wins50_curr = wins50_curr + sum(mychar_tab[timestamp %in% all_timestamps[1:50]]$victory)
  wins25_curr = wins25_curr + sum(mychar_tab[timestamp %in% all_timestamps[1:25]]$victory)
}
df["Rotating"] = c(winsall_curr/tot_curr,wins50_curr/50,wins25_curr/25)

#save winrate stats over time
write.csv(df,file = file.path(my_working_dir,"winrate_history",paste0("winrate_",unlist(strsplit(as.character(Sys.time()), split=" "))[1],".csv")))

#plot this
plotdata = melt(data.table(df,keep.rownames = T),id.vars="rn")
colnames(plotdata) = c("Number of Runs","Character","Winrate")
plotdata$Winrate = plotdata$Winrate*100
plotdata$`Number of Runs` = factor(plotdata$`Number of Runs`,levels = c("Overall","Last50","Last25"),ordered=T)

p1 = ggplot(plotdata, aes(fill=`Number of Runs`, y=Winrate, x=Character)) + 
  geom_bar(position="dodge", stat="identity") +
  scale_fill_manual(values=c("#7dcfb6", "#f79256", "#fbd1a2")) +
  ylab("Winrate (%)")

ggplotly(p1)
#TODO - try to annotate this with the total number of runs for each bar

Calculate current and max winstreaks

curr_winstreaks = max_winstreaks = curr_lossstreaks = max_lossstreaks = vector("numeric")

## individual character winstreaks ##

for(char in char_vec){
  mychar_tab = allchar_list[[char]]
  mychar_tab = mychar_tab[order(timestamp,decreasing=T)]
  my_rle = rle(mychar_tab$victory)
  all_wins = my_rle$length[my_rle$values == TRUE]
  all_losses = my_rle$length[my_rle$values == FALSE]
  
  #get current streaks
  if(mychar_tab$victory[1]){ #if most recent run was a victory
    curr_winstreaks = append(curr_winstreaks,all_wins[1])
    curr_lossstreaks = append(curr_lossstreaks,0)
  }else{
    curr_winstreaks = append(curr_winstreaks,0)
    curr_lossstreaks = append(curr_lossstreaks,all_losses[1])
  }
  
  #get max streaks
  max_winstreaks = append(max_winstreaks, max(all_wins))
  max_lossstreaks = append(max_lossstreaks, max(all_losses))
}

## rotating winstreak, and determine which character I need to play next to continue rotating ##

get_rotating_winstreak <- function(runtab, get_next_char=FALSE){
  runtab = runtab[order(timestamp,decreasing=F)] #forward chronological order
  #remove any consecutive runs with the same character (e.g. 2 wins with ironclad and 1 win with silent = rotating streak of 2)
  my_char_vec = runtab$character
  my_char_vec = rle(my_char_vec)$values
  my_char_vec = sapply(my_char_vec, function(x) switch(x,"Ironclad"=1,"Silent"=2,"Defect"=3,"Watcher"=4))
  #return 1 if the length of char_vec is 1
  if(length(my_char_vec) <= 1){
    if(get_next_char){
      char_next = switch(my_char_vec[1],"1"="Silent","2"="Defect","3"="Watcher","4"="Ironclad")
      return(list(1,char_next))
    }else{
      return(1)
    }
  }
  #count max length that follows the correct ordering 
  nwins_curr = nwins_max = 1
  most_recent_char = char_curr_max = my_char_vec[1]
  for(i in 2:length(my_char_vec)){
    char_curr = my_char_vec[i]
    if(char_curr != 1){
      if(char_curr - most_recent_char == 1){ #correct order
        nwins_curr = nwins_curr+1
      }else{ #incorrect order - reset
        nwins_curr = 1
      }
    }else if(char_curr == 1){
      if(most_recent_char == 4){ #correct order
        nwins_curr = nwins_curr+1
      }else{ #incorrect order - reset
        nwins_curr = 1
      }
    }
    
    if(nwins_curr > nwins_max){
      nwins_max = nwins_curr
      #get current character
      char_curr_max = char_curr
    }
    most_recent_char = char_curr
  }
  
  if(get_next_char){
    char_next = switch(char_curr_max,"1"="Silent","2"="Defect","3"="Watcher","4"="Ironclad")
    return(list(nwins_max,char_next))
  }else{
    return(nwins_max)
  }
}

alltab = rbindlist(allchar_list,fill=TRUE)
alltab = alltab[order(timestamp,decreasing=T)]
consec_ind = alltab[ , .(start = .I[1], end = .I[.N]), by = rleid(victory)][, rleid := NULL][]

#check for current rotating winstreak
curr_runs = alltab[as.numeric(consec_ind[1,1]):as.numeric(consec_ind[1,2])]
if(curr_runs$victory[1]){ #most recent set of runs are victories, now need to determine how many of those count towards the rotating winstreak
  curr_rot_info = get_rotating_winstreak(curr_runs,get_next_char=TRUE)
  curr_winstreaks = append(curr_winstreaks,curr_rot_info[[1]])
}else{ #most recent run is a loss
  curr_winstreaks = append(curr_winstreaks,0)
}

#check for max rotating winstreak
n_rot_wins_max = 0
for(i in 1:nrow(consec_ind)){
  curr_runs = alltab[as.numeric(consec_ind[i,1]):as.numeric(consec_ind[i,2])]
  if(curr_runs$victory[1]){
    n_rot_wins = get_rotating_winstreak(curr_runs)
    if(n_rot_wins > n_rot_wins_max){n_rot_wins_max = n_rot_wins}
  }
}
max_winstreaks = append(max_winstreaks,n_rot_wins_max)

CURRENT ROTATING STREAK:
[1] “There is no active rotating streak ” There is no active rotating streak

CURRENT WIN STREAKS:

CURRENT LOSS STREAKS:
The Ironclad is currently on a loss streak of 1
The Silent is currently on a loss streak of 1
The Defect is currently on a loss streak of 2
The Watcher is currently on a loss streak of 1

PERSONAL BEST/WORST STATS:
The max rotating streak is 5 consecutive wins
The Ironclad’s max streaks are 4 consecutive wins and 13 consecutive losses
The Silent’s max streaks are 3 consecutive wins and 17 consecutive losses
The Defect’s max streaks are 2 consecutive wins and 20 consecutive losses
The Watcher’s max streaks are 3 consecutive wins and 5 consecutive losses